using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;


namespace MIMP
{
    public class StringEventArgs : EventArgs
    {
        public StringEventArgs(string eventMessage)
        {
            this.msg = eventMessage;
        }

        private string msg;

        public string Message
        {
            get { return msg; }
        }
    }
    /// <summary>
    /// A screen which contains a header and a text box for the user to type into.
    /// </summary>
    class KeyboardInputScreen : GameScreen
    {
        string header;
        string text;
        Texture2D background;
        UIButtonDisplay ok;
        UIButtonDisplay cancel;
        Vector2 origin;
        Rectangle backgroundRectangle;
        Rectangle textRectangle;
        TimeSpan inputInterval;
        TimeSpan repeatInterval;
        TimeSpan timeSinceLastKeyPress;
        Keys pressed;

        /// <summary>
        /// The text in the input text box. Will be modified by the user typing. 39 character maximum.
        /// </summary>
        public string Text
        {
            get { return text; }
            set
            {
                if (value.Length > 50)
                {
                    throw new ArgumentException("KeyboardInputScreen.Text may not be more than 55 characters.");
                }
                else
                {
                    text = value;
                }
            }
        }
        /// <summary>
        /// The string that appears above the text box, used to give brief instructions to the 
        /// user about what to type. 30 character maximum. 
        /// </summary>
        public string Header
        {
            get { return header; }
            set
            {
                if (value.Length > 30)
                {
                    throw new ArgumentException("KeyboardInputScreen.Header may not be more than 40 characters.");
                }
                else
                {
                    header = value;
                }
            }
        }

        public event EventHandler<StringEventArgs> Accepted;
        public event EventHandler<EventArgs> Cancelled;

        /// <summary>
        /// /// Constructs a new KeyboardInputScreen with the specified header and no text in the textbox.
        /// </summary>
        /// <param name="header"></param>
        public KeyboardInputScreen(string header) : this(header, "") { }

        /// <summary>
        /// Constructs a new KeyboardInputScreen with the specified header and initial textbox text.
        /// </summary>
        public KeyboardInputScreen(string header, string initialText)
        {
            this.header = header;
            this.text = initialText;

            IsPopup = true;

            TransitionOnTime = TimeSpan.FromSeconds(0.2);
            TransitionOffTime = TimeSpan.FromSeconds(0.2);
        }


        /// <summary>
        /// Loads graphics content for this screen. This uses the shared ContentManager
        /// provided by the Game class, so the content will remain loaded forever.
        /// Whenever a subsequent MessageBoxScreen tries to load this same content,
        /// it will just get back another reference to the already loaded data.
        /// </summary>
        public override void LoadContent()
        {
            Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
            Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
            origin = viewportSize / 4;
            backgroundRectangle = new Rectangle((int)origin.X, (int)origin.Y * 3 / 2, (int)origin.X * 2, 150);
            textRectangle = new Rectangle(backgroundRectangle.Left + 10, backgroundRectangle.Top + 50, backgroundRectangle.Width - 20, 40);
            inputInterval = TimeSpan.FromSeconds(2);
            repeatInterval = TimeSpan.FromSeconds(0.25);
            timeSinceLastKeyPress = TimeSpan.Zero;

            ContentManager content = ScreenManager.Game.Content;

            background = content.Load<Texture2D>(MIMPGame.CONTENT_DIR + "blank");
            Texture2D check = content.Load<Texture2D>(MIMPGame.CONROL_PANEL_SPRITES_DIR + "check-button");
            Texture2D canc = content.Load<Texture2D>(MIMPGame.CONROL_PANEL_SPRITES_DIR + "cancel-button");
            ok = new UIButtonDisplay(backgroundRectangle.Right - 10 - check.Width, backgroundRectangle.Bottom - 10 - check.Height, 1, check, Keys.Enter, UIButtons.EndTurn);
            cancel = new UIButtonDisplay(backgroundRectangle.Right - 20 - check.Width - canc.Width, backgroundRectangle.Bottom - 10 - canc.Height, 1, canc, Keys.Escape, UIButtons.Menu);
        }

        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            timeSinceLastKeyPress += gameTime.ElapsedGameTime;
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
        }

        /// <summary>
        /// Responds to user input, accepting or cancelling the input screen, and updating the displayed text.
        /// </summary>
        public override void HandleInput(InputState input)
        {
            ok.SetIsMouseOver(input.CurrentMouseState);
            cancel.SetIsMouseOver(input.CurrentMouseState);

            UpdateText(input);

            if (input.IsNewKeyRelease(ok.ShortcutKey) || (input.IsNewLeftMouseClick() && ok.IsMouseOver))
            {
                // Raise the accepted event, then exit the message box.
                if (Accepted != null)
                    Accepted(this, new StringEventArgs(text));

                ExitScreen();
            }
            else if (input.IsNewKeyRelease(cancel.ShortcutKey) || (input.IsNewLeftMouseClick() && cancel.IsMouseOver))
            {
                // Raise the cancelled event, then exit the message box.
                if (Cancelled != null)
                    Cancelled(this, EventArgs.Empty);

                ExitScreen();
            }
        }

        void UpdateText(InputState input)
        {
            //Loop through all keys that are down and find one that is a letter, number, or space, or the backspace key
            Keys[] down = input.CurrentKeyboardStates[0].GetPressedKeys();
            Keys curKey = Keys.Zoom;
            foreach (Keys key in down)
            {
                if (Char.IsLetterOrDigit(GetKeyString(key)) || key.Equals(Keys.Space) || key.Equals(Keys.Back))
                {
                    curKey = key;
                }
            }
            
            TimeSpan intervalToCheck = curKey == pressed ? repeatInterval : inputInterval;
            pressed = curKey;

            //If curKey is still Zoom at this point, then no keys that we care about are down
            if (curKey == Keys.Zoom)
            {
                return;
            }

            if (input.IsNewKeyPress(pressed) || timeSinceLastKeyPress > intervalToCheck)
            {
                timeSinceLastKeyPress = TimeSpan.Zero;
                //handle backspace
                if (input.IsNewKeyPress(Keys.Back) && text.Length > 0)
                {
                    text = text.Substring(0, text.Length - 1);
                    return;
                }

                //see if either Shift key is down
                bool shifted = input.IsKeyDown(Keys.LeftShift) || input.IsKeyDown(Keys.RightShift);
                foreach (Keys key in down)
                {
                    shifted = key.Equals(Keys.LeftShift) || key.Equals(Keys.RightShift);
                }

                //write appropriate character
                char keyName = GetKeyString(pressed);
                Text += shifted ? Char.ToUpper(keyName) : Char.ToLower(keyName);
            }
        }

        char GetKeyString(Keys key)
        {
            string s = key.ToString();
            //All Key names that are 1 char long are alphabetic characters
            if (s.Length == 1)
            {
                return s[0];
            }
            else if ((key >= Keys.D0 && key <= Keys.D9) || (key >= Keys.NumPad0 && key <= Keys.NumPad9))
            {
                return s[s.Length - 1];
            }
            else if (key.Equals(Keys.Space))
            {
                return ' ';
            }
            else
            {
                return Char.MaxValue;
            }
        }

        /// <summary>
        /// Draws the message box.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
            SpriteFont font = ScreenManager.MessageBoxFont;

            // Darken down any other screens that were drawn beneath the popup.
            ScreenManager.FadeBackBufferToBlack(TransitionAlpha * 2 / 3);

            // Fade the popup alpha during transitions.
            float alpha = TransitionAlpha / 255;
            Color backColor = new Color(150, 150, 150, TransitionAlpha);
            Color textBoxColor = new Color(255, 255, 255, TransitionAlpha);
            Color textColor = new Color(0, 0, 0, TransitionAlpha);
            spriteBatch.Begin();

            // Draw the background rectangle.
            spriteBatch.Draw(background, backgroundRectangle, backColor);

            // Draw the header text.
            spriteBatch.DrawString(font, header, new Vector2(backgroundRectangle.X + 10, backgroundRectangle.Y + 10), textColor);

            //Draw the input box and text.
            spriteBatch.Draw(background, textRectangle, textBoxColor);
            Vector2 textPosition = new Vector2(textRectangle.X + 1, (textRectangle.Y + textRectangle.Height / 2) - font.MeasureString(text).Y / 2);
            spriteBatch.DrawString(font, text, textPosition, textColor);

            // Draw the buttons.
            spriteBatch.Draw(ok.Sprite, ok.Origin, textBoxColor);
            spriteBatch.Draw(cancel.Sprite, cancel.Origin, textBoxColor);

            spriteBatch.End();
        }
    }
}
